using System;
using System.Data;
using System.Data.Objects;
using System.Linq;
using System.Linq.Expressions;
using WRSManagementSystem.DomainModel.Repositories;

namespace WRSManagementSystem.DataAccess.Repositories
{
    /// <summary>
    /// A generic repository for working with data in the database
    /// </summary>
    /// <typeparam name="T">A POCO that represents an Entity Framework entity</typeparam>
    public class EFRepository<T> : IRepository<T> where T : class
    {
        /// <summary>
        /// The context object for the database
        /// </summary>
        private readonly ObjectContext _context;

        /// <summary>
        /// The IObjectSet that represents the current entity.
        /// </summary>
        private readonly IObjectSet<T> _objectSet;

        private bool _disposed;

        /// <summary>
        /// Initializes a new instance of the DataRepository class
        /// </summary>
        /// <param name="context">The Entity Framework ObjectContext</param>
        public EFRepository(ObjectContext context)
        {
            _context = context;
            _objectSet = _context.CreateObjectSet<T>();
        }

        #region IRepository<T> Members

        public int Count()
        {
            return _objectSet.Count();
        }

        public int Count(Expression<Func<T, bool>> expression)
        {
            return _objectSet.Where(expression).Count();
        }

        public void Add(T entity)
        {
            if (entity == null)
                throw new ArgumentNullException("entity");

            _objectSet.AddObject(entity);
        }

        public void Remove(T entity)
        {
            if (entity == null)
                throw new ArgumentNullException("entity");

            _objectSet.DeleteObject(entity);
        }

        public void Save()
        {
            //TODO: optimistic concurency handling
            _context.SaveChanges();
        }

        public void Update(T entity)
        {
            //TODO: optimistic concurency handling
            _objectSet.Attach(entity);
            _context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
            _context.SaveChanges();
        }

        public T FindOne(Expression<Func<T, bool>> expression)
        {
            return _objectSet.Where(expression).FirstOrDefault();
        }

        public IQueryable<T> Find()
        {
            return _objectSet;
        }

        public IQueryable<T> Find(Expression<Func<T, bool>> expression)
        {
            return _objectSet.Where(expression);
        }

        #endregion

        public void Dispose()
        {
            DisposeObject(true);
            GC.SuppressFinalize(this);
        }

        private void DisposeObject(bool disposing)
        {
            if (_disposed)
            {
                return;
            }
            if (disposing)
            {
                if (_context != null)
                    _context.Dispose();
            }
            _disposed = true;
        }
    }
}